﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Google.GData.Calendar;
using Google.GData.Extensions;
using WPFLocalizeExtension.Engine;

namespace Journal.Helpers
{
    /// <summary>
    /// This static class contains various useful methods used in various parts of this application.
    /// </summary>
    public class Utilities
    {

        /// <summary>
        /// Switches the localization culture.
        /// </summary>
        /// <param name="culture">ISO code of the new culture.</param>
        public static void SwitchCulture(string culture)
        {
            CultureInfo ci = CultureInfo.InvariantCulture;
            try
            {
                ci = new CultureInfo(culture);
            }
            catch (CultureNotFoundException)
            {
                try
                {
                    // Try language without region
                    ci = new CultureInfo(culture.Substring(0, 2));
                }
                catch (Exception)
                {
                    ci = CultureInfo.InvariantCulture;
                }
            }
            finally
            {
                LocalizeDictionary.Instance.Culture = ci;
            }
        }

        /// <summary>
        /// Looks up a localized string.
        /// </summary>
        /// <param name="resourceAssembly">Assembly containing the resource.</param>
        /// <param name="resourceDictionary">Dictionary containing the resource.</param>
        /// <param name="resourceKey">Identifier of the resource.</param>
        /// <returns>The value of the resource; or the key prefixed with '@' if not found.</returns>
        public static string GetLocalizedValue(string resourceAssembly, string resourceDictionary, string resourceKey)
        {
            try
            {
                return LocalizeDictionary.Instance.GetLocalizedObject<string>(
                             resourceAssembly,
                             resourceDictionary,
                             resourceKey,
                             CultureInfo.CurrentCulture);
            }
            catch (Exception)
            {
                return "@" + resourceKey;
            }
        }

        /// <summary>
        /// Create application directory 'JournalApp' in if it doesn't already exist.
        /// </summary>
        /// <param name="dir">Name of directory. Can be whole path.</param>
        public static void CreateDirectory(String dir)
        {
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
        }

        /// <summary>
        /// Extracts calendar ID from calendar's feed URI string.
        /// </summary>
        /// <param name="calendarURI">calendar's feed URI string</param>
        /// <returns>string representing calendar ID (i.e. "foo123456789@bar.gmail.com")</returns>
        public static string ExtractCalendarId(string calendarURI)
        {
            return calendarURI.Substring(calendarURI.LastIndexOf('/') + 1);
        }

        /// <summary>
        /// Builds feed URI for retrieving events for particular calendar.
        /// </summary>
        /// <param name="calendarID">calendar ID</param>
        /// <returns>feed URI string
        /// (i.e. "https://www.google.com/calendar/feeds/foo123456789@bar.gmail.com/private/full")</returns>
        public static string BuildEventsFeedURI(string calendarID)
        {
            return "https://www.google.com/calendar/feeds/" + calendarID + "/private/full";
        }

        /// <summary>
        /// Determine whether the given DateTime objects have same day in year.
        /// </summary>
        /// <param name="date1">First date</param>
        /// <param name="date2">Second date</param>
        /// <returns>true if the dates have same day in year, otherwise false</returns>
        public static bool AreSameDaysInYear(DateTime date1, DateTime date2)
        {
            if (date1.DayOfYear.Equals(date2.DayOfYear) && date1.Year.Equals(date2.Year))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Returns instance of Google.GData.Calendar.CalendarEntry object with
        /// appropriate properties set from Domain.Calendar object.
        /// </summary>
        /// <param name="calendar">Instance of Domain.Calendar object.</param>
        /// <param name="isNew">If set to true, new instance of CalendarEntry is created.</param>
        /// <returns>CalendarEntry that contains data from calendar parameter.</returns>
        public static CalendarEntry GetGoogleCalendarEntry(Domain.Calendar calendar, bool isNew)
        {
            CalendarEntry calendarEntry;
            if (isNew)
            {
                calendarEntry = new CalendarEntry();
            }
            else
            {
                calendarEntry = calendar.GoogleCalendar;
            }

            calendarEntry.Title.Text = calendar.Name;
            calendarEntry.Summary.Text = calendar.Description;
            calendarEntry.Location = new Where("", "", calendar.Location);
            return calendarEntry;
        }

        /// <summary>
        /// Returns instance of Google.GData.Calendar.EventEntry object with
        /// appropriate properties set from Domain.Event object.
        /// </summary>
        /// <param name="thisEvent">Instance of Domain.Event object.</param>
        /// <param name="isNew">If set to true, new instance of EventEntry is created.</param>
        /// <returns>EventEntry that contains data from calendar parameter.</returns>
        public static EventEntry GetGoogleEventEntry(Domain.Event thisEvent, bool isNew)
        {
            EventEntry eventEntry;
            if (isNew)
            {
                eventEntry = new EventEntry();
            }
            else
            {
                eventEntry = thisEvent.GoogleEvent;
            }

            eventEntry.Title.Text = thisEvent.Title;
            eventEntry.Content.Content = thisEvent.Description;
            eventEntry.Locations.Clear();
            eventEntry.Locations.Add(new Where("", "", thisEvent.Location));
            eventEntry.Times.Clear();
            eventEntry.Times.Add(new When(thisEvent.StartTime, thisEvent.EndTime, thisEvent.AllDay));
            return eventEntry;
        }

        /// <summary>
        /// Determines if google calendar is user's primary calendar or not.
        /// (Primary (or default) calendar cannot be deleted)
        /// </summary>
        /// <param name="calendar">Object holding google calendar.</param>
        /// <returns>true if calendar is primary, otherwise false</returns>
        public static bool IsGoogleCalendarPrimary(CalendarEntry calendar)
        {
            string userName = calendar.Service.Credentials.Username;
            string calendarID = ExtractCalendarId(calendar.SelfUri.Content);
            calendarID = calendarID.Substring(0, calendarID.IndexOf('%'));
            return userName.Equals(calendarID);
        }

        /// <summary>
        /// Logs given exception parameter to file.
        /// </summary>
        /// <param name="e">Exception to log.</param>
        public static void LogException(Exception e)
        {
            string logEntry = "Exception was thrown!\nMessage: " + e.Message + "\nInnerMessage: " + e.InnerException.Message + "\n" + e.StackTrace;
            JournalApp.GetLog(JournalApp.LogType.FileLogger).Error(logEntry);
        }

        /// <summary>
        /// Validates parameter 'time' (string). Valid strings representing hours and minutes are:
        /// hh:mm, hh:m, h:mm, h:m. If valid new DateTime is returned with properties of parameter 'date'
        /// and hours and minutes are set to this new DateTime object.
        /// </summary>
        /// <param name="date">Properties of this param are used in returning DateTime.</param>
        /// <param name="time">Representation of time (hours and minutes)</param>
        /// <returns>DateTime with properties of date param, hours and minutes are parsed from time param.</returns>
        public static DateTime ValidateAndGetTime(DateTime date, string time)
        {
            Regex regex = new Regex("([0-9][0-9]|[0-9]):([0-9][0-9]|[0-9])");

            if (regex.Match(time).Success)
            {
                string[] timeParts = time.Split(':');
                int hours = int.Parse(timeParts[0]);
                int minutes = int.Parse(timeParts[1]);
                if (hours < 24 && minutes < 60)
                {
                    return new DateTime(date.Year, date.Month, date.Day, hours, minutes, 0);
                }
            }

            return new DateTime(0);
        }

        /// <summary>
        /// Parse time element (hours or minutes) from int to string in format 'hh' or 'mm'.
        /// (For example, 5 minutes are parsed and final string looks like '05', etc.)
        /// </summary>
        /// <param name="timeElement">time element (hours/minutes)</param>
        /// <returns>string representing minutes or hours</returns>
        public static string ParseTimeElementForTextBox(int timeElement)
        {
            return timeElement < 10 ? "0" + timeElement : timeElement.ToString();
        }
    }
}
